#![allow(dead_code)]
/*
example
fn main() {
    unsafe {
        let mut a: VecCopy<i32> = VecCopy::new();
        for i in 0..10 {
            a.push(i);
        }

        // Copy handle — no drop, no ownership
        let b = a;             // Copy
        let c = b.clone();     // Also Copy

        // Do some work...
        let _x = c.get(5);

        // Run GC, keeping 'a' and 'c' memory alive
        GcAlloc::collect(&[a.as_ptr(), c.as_ptr()]);

        // If you drop all handles and don't mark them as roots, GC will free their memory.
        // For example:
        // GcAlloc::collect(&[]); // would sweep all unmarked blocks
    }
}

*/

use std::alloc::{Layout, alloc, dealloc, realloc};
use std::cell::UnsafeCell;
use std::ptr::NonNull;

struct Block {
    ptr: NonNull<u8>,
    size: usize, // bytes
    layout: Layout,
    mark: bool,
}

pub struct GcAlloc {
    blocks: UnsafeCell<Vec<Block>>,
}

unsafe impl Send for GcAlloc {}
unsafe impl Sync for GcAlloc {}

static GC: GcAlloc = GcAlloc {
    blocks: UnsafeCell::new(Vec::new()),
};

impl GcAlloc {
    unsafe fn register(ptr: *mut u8, layout: Layout) {
        unsafe {
            let nn = NonNull::new(ptr).expect("alloc returned null");
            let blocks = &mut *GC.blocks.get();
            blocks.push(Block {
                ptr: nn,
                size: layout.size(),
                layout,
                mark: false,
            });
        }
    }

    unsafe fn find_block_index(ptr: *mut u8) -> Option<usize> {
        unsafe {
            let blocks = &mut *GC.blocks.get();
            blocks.iter().position(|b| b.ptr.as_ptr() == ptr)
        }
    }

    pub unsafe fn alloc_array<T>(n: usize) -> *mut T {
        unsafe {
            let layout = Layout::array::<T>(n).unwrap();
            let p = alloc(layout) as *mut T;
            if p.is_null() {
                std::alloc::handle_alloc_error(layout);
            }
            Self::register(p as *mut u8, layout);
            p
        }
    }

    pub unsafe fn realloc_array<T>(ptr: *mut T, old: usize, new: usize) -> *mut T {
        unsafe {
            // NOTE: may move; we must update the block record
            let old_layout = Layout::array::<T>(old).unwrap();
            let new_layout = Layout::array::<T>(new).unwrap();
            let new_size = new_layout.size();
            let new_ptr = realloc(ptr as *mut u8, old_layout, new_size) as *mut T;
            if new_ptr.is_null() {
                std::alloc::handle_alloc_error(new_layout);
            }

            // update block record
            let blocks = &mut *GC.blocks.get();
            if let Some(idx) = blocks.iter().position(|b| b.ptr.as_ptr() == ptr as *mut u8) {
                blocks[idx].ptr = NonNull::new_unchecked(new_ptr as *mut u8);
                blocks[idx].size = new_size;
                blocks[idx].layout = new_layout;
            } else {
                // if not found (shouldn't happen), register new
                Self::register(new_ptr as *mut u8, new_layout);
            }

            new_ptr
        }
    }

    // Mark a pointer as live. You pass any pointer inside the block; here we match by block base.
    pub unsafe fn mark(ptr: *const u8) {
        unsafe {
            let blocks = &mut *GC.blocks.get();
            if let Some(idx) = blocks.iter().position(|b| b.ptr.as_ptr() == ptr as *mut u8) {
                blocks[idx].mark = true;
            }
        }
    }

    // Clear all marks (start of GC cycle).
    pub unsafe fn clear_marks() {
        unsafe {
            for b in (&mut *GC.blocks.get()).iter_mut() {
                b.mark = false;
            }
        }
    }

    // Sweep unmarked blocks.
    pub unsafe fn sweep() {
        unsafe {
            let blocks = &mut *GC.blocks.get();
            let mut i = 0;
            while i < blocks.len() {
                if !blocks[i].mark {
                    let b = blocks.remove(i);
                    dealloc(b.ptr.as_ptr(), b.layout);
                } else {
                    i += 1;
                }
            }
        }
    }

    // One-shot GC: provide roots (pointers you want to keep), we mark and sweep.
    pub unsafe fn collect(roots: &[*const u8]) {
        unsafe {
            Self::clear_marks();
            for &r in roots {
                Self::mark(r);
            }
            Self::sweep();
        }
    }
}
